package com.fuyin.service.superadmin.impl;

import cn.hutool.core.lang.Validator;
import com.fuyin.commons.enums.ErrorCodeEnum;
import com.fuyin.commons.response.BaseResponse;
import com.fuyin.commons.response.ErrorCode;
import com.fuyin.commons.utils.JwtTokenUtil;
import com.fuyin.commons.utils.MyUtils;
import com.fuyin.dao.OperationLogDao;
import com.fuyin.dao.UserDao;
import com.fuyin.dto.CommonUserDetails;
import com.fuyin.dto.params.LoginParam;
import com.fuyin.dto.vo.LoginResult;
import com.fuyin.entity.OperationLog;
import com.fuyin.entity.PmValidate;
import com.fuyin.entity.User;
import com.fuyin.service.RedisService;
import com.fuyin.service.ValidateServiceImpl;
import com.fuyin.service.superadmin.SuperAdminService;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @author yuanxilan (yuanxilan@keep.com)
 * @Description
 * @date 2021年11月15日 8:50 下午
 * @since
 */
@Service
@Slf4j
public class SuperAdminServiceImpl implements SuperAdminService {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private OperationLogDao logDao;

    @Autowired
    private UserDao userDao;

    @Autowired
    private RedisService redisService;

    @Autowired
    private OperationLogDao operationLogDao;

    @Autowired
    private ValidateServiceImpl validateService;

    @Value("${spring.mail.username}")
    private String from;

    @Autowired
    private JavaMailSender mailSender;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Value("${password.pattern}")
    private String passwordPattern;

    @Override
    public BaseResponse login(LoginParam param) {
        if (Objects.isNull(param) || StringUtils.isBlank(param.getUserName()) || StringUtils.isBlank(param.getPassword())) {
            log.error("com.fuyin.service.user.impl.UserServiceImpl#login param:{}", param);
            return BaseResponse.error(ErrorCodeEnum.BAD_REQUEST);
        }
        String token = "";
        User user = userDao.getUserByUserName(param.getUserName());
        if (Objects.isNull(user)) {
            return BaseResponse.error(ErrorCode.ACCOUNT_NOT_FOUND, "账号不存在");
        } else if (user.getState() == -1) {
            return BaseResponse.error(ErrorCode.ACCOUNT_NOT_USED, "账号未启动");
        } else if (!passwordEncoder.matches(param.getPassword(), user.getPassword())) {
            return BaseResponse.error(ErrorCode.ACCOUNT_ERROR, "账号密码错误");
        } else if (user.getType() != 2) {
            return BaseResponse.error(ErrorCode.FORBIDDEN, "非超级管理员请求用户接口");
        }
        if (redisService.exists(user.getUserName())) {
            redisService.delete(redisService.get(user.getUserName()));
        }
        CommonUserDetails userDetails = new CommonUserDetails(user);
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        token = jwtTokenUtil.generateToken(userDetails);
        redisService.set(token, user.getType());
        redisService.set(user.getUserName(), token);
        return BaseResponse.success(new LoginResult(token, user.getType()));
    }

    @Override
    public BaseResponse updatePassword(String token, String userName, String password, HttpServletRequest request) {
        MyUtils.checkParams("账户名和新密码不能为空", userName, password);
        token = token.substring(this.tokenHead.length());
        OperationLog operationLog = new OperationLog();
        operationLog.setUserName(jwtTokenUtil.getUserNameFromToken(token));
        operationLog.setType("操作日志");
        operationLog.setOperationTime(new Date());
        operationLog.setDescription("修改用户密码，用户名为: " + userName);
        operationLog.setIp(MyUtils.getIpAddr(request));
        User user = userDao.getUserByUserName(userName);
        if (Objects.isNull(user)) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(ErrorCode.ACCOUNT_NOT_FOUND, "账号不存在");
        } else if (passwordEncoder.matches(password, user.getPassword())) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(ErrorCode.ACCOUNT_NEW_OLD_PSW_SAME, "新旧密码不能相同");
        }
        if (!password.matches(passwordPattern)) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(ErrorCode.PASSWORD_NOT_MATCH_PATTERN, "密码不符合规范");
        }
        password = passwordEncoder.encode(password);
        userDao.updatePassword(userName, password);
        if (redisService.exists(userName)) {
            redisService.delete(redisService.get(userName));
            redisService.delete(userName);
        }
        operationLog.setResult("修改成功");
        logDao.save(operationLog);
        return BaseResponse.success();
    }

    @Override
    public BaseResponse logout(String token) {
        token = StringUtils.substring(token, tokenHead.length());
        redisService.delete(token);
        return BaseResponse.success();
    }

    @Override
    public BaseResponse<ArrayList<User>> getAllUsers(String token, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = userDao.getUserList(Arrays.asList(0, 1, 2));
        ArrayList<User> ans = (ArrayList<User>) users;
        ans.forEach(x -> x.setPassword(""));
        return BaseResponse.success(ans);
    }

    @Override
    public BaseResponse updateUserInfo(String token, User user, HttpServletRequest request) {
        MyUtils.checkParams("用户名不能为空", user.getUserName());
        if (Objects.nonNull(user.getType()) && (user.getType() < 0 || user.getType() > 2)) {
            return BaseResponse.error(400, "错误的用户类型");
        }
        token = token.substring(this.tokenHead.length());
        OperationLog operationLog = new OperationLog();
        operationLog.setUserName(jwtTokenUtil.getUserNameFromToken(token));
        operationLog.setType("操作日志");
        operationLog.setOperationTime(new Date());
        User user1 = userDao.getUserByUserName(user.getUserName());
        if (Objects.isNull(user1)) {
            operationLog.setResult("修改失败");
            logDao.save(operationLog);
            return BaseResponse.error(ErrorCode.ACCOUNT_NOT_FOUND, "查找不到该用户");
        }
        if (user1.getType() == 0) {
            operationLog.setDescription("修改用户信息，用户名为: " + user.getUserName());
        } else if (user1.getType() == 1) {
            operationLog.setDescription("修改管理员信息，用户名为: " + user.getUserName());
        } else if (user1.getType() == 2) {
            operationLog.setDescription("修改超级管理员信息，用户名为：" + user.getUserName());
        }
        operationLog.setIp(MyUtils.getIpAddr(request));
        if (Objects.nonNull(user.getEmail()) && !StringUtils.equals(user.getEmail(), user1.getEmail())) {
            String email = user.getEmail();
            try {
                if (validateService.sendValidateLimitation(email, 20, 1)) {
                    // 若允许重置密码，则在pm_validate表中插入一行数据，带有token
                    PmValidate pmValidate = new PmValidate();
                    validateService.insertNewResetRecord(pmValidate, user, UUID.randomUUID().toString(), "changeEmail");
                    // 设置邮件内容
                    String appUrl = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/fuyin/api/change_email";
                    MimeMessage mimeMessage = mailSender.createMimeMessage();
                    // multipart模式
                    MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "utf-8");
                    mimeMessageHelper.setTo(email);
                    mimeMessageHelper.setFrom(from);
                    mimeMessageHelper.setSubject("修改邮箱");
                    StringBuilder sb = new StringBuilder();
                    sb.append("<html><head></head>");
                    sb.append("<body><h1>点击下面的链接确认修改邮箱，有效期为 5 分钟</h1>" +
                            "<a href = " + appUrl + "?token=" + pmValidate.getResetToken() + ">" + appUrl + "?token=" + pmValidate.getResetToken() + "</a></body>");
                    sb.append("</html>");
                    // 启用html
                    mimeMessageHelper.setText(sb.toString(), true);
                    validateService.sendPasswordResetEmail(mimeMessage);
                    redisService.set(pmValidate.getResetToken(), user);
                } else {
                    return BaseResponse.error(ErrorCode.OPERATION_FREQUENCY, "操作过于频繁，请稍后再试！");
                }
            } catch (Exception e) {
                log.error("com.fuyin.service.admin.impl.AdminServiceImpl.updateUserInfo e:{}", e.getMessage());
            }
        }
        user.setEmail("");
//        user.setPassword("");
        if (StringUtils.isNotBlank(user.getPassword()) && !Validator.hasChinese(user.getPassword())) {
            if (!user.getPassword().matches(passwordPattern)) {
                user.setPassword(StringUtils.EMPTY);
            } else {
                user.setPassword(passwordEncoder.encode(user.getPassword()));
                if (redisService.exists(user.getUserName())) {
                    redisService.delete(redisService.get(user.getUserName()));
                    redisService.delete(user.getUserName());
                }
            }
        }
        userDao.updateUser(user);
        operationLog.setResult("修改成功");
        logDao.save(operationLog);
        return BaseResponse.success();
    }

    @Override
    public BaseResponse<ArrayList<OperationLog>> getOperationLogs(String token, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        ArrayList<OperationLog> operationLogs = (ArrayList<OperationLog>) operationLogDao.getOperationLogs();
        return BaseResponse.success(operationLogs);
    }
}
